package limedia.sdk.api.realtime;

import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;

import com.limedia.explorer.constant.Constant;
import com.limedia.explorer.enums.Enums.ClientNo;
import com.limedia.explorer.service.DeviceDiscoverService;

import android.R.integer;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

/**
 * RTP native
 * @author ThunderSoft
 *
 */
public class TSRTSPNative {
	
	private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/video.H264";
	private static TSRTSPNative mTsrtpNative = null;
	
	private static final int TYPE_FRAME_SPS = 7;
	private static final int TYPE_FRAME_I = 5;
	
	private volatile boolean isUploading = false;
    private volatile boolean isAboutToStartUploading = false;
    private volatile boolean isAboutToSwitchClient = false;
    
    private ClientNo curUploadClientNo = ClientNo.Client_INVALID;
    private ClientNo swtchToClientNo = ClientNo.Client_INVALID;
	
    private boolean isInitiated1 = false;
    private boolean isInitiated2 = false;
    private boolean isInitiated3 = false;
    
	private SynchronousQueue<TSPcmData> mVideoPcmData1 = null;
	private SynchronousQueue<TSPcmData> mVideoPcmData2 = null;
	private SynchronousQueue<TSPcmData> mVideoPcmData3 = null;
	
	private SynchronousQueue<TSPcmData> mAudioPcmData1 = null;
	private SynchronousQueue<TSPcmData> mAudioPcmData2 = null;
	private SynchronousQueue<TSPcmData> mAudioPcmData3 = null;
	
	private String rtspUrl1 = null;
	private String rtspUrl2 = null;
	private String rtspUrl3 = null;
	
	private Handler mHandler = null;
	
	private ExecutorService executorService = null;

    static {
    	System.loadLibrary("RTSPClient");
    	//System.load("RTSPClient");
    }
    
    public native void native_init(String etUrl1, String etUrl2, String etUrl3, String etUrl4, String etUrl5);

	public native void native_deinit(int mask);

    public native void native_forward(int channel);
    /**
     * get TSRTPNative instance
     * @param pDatas video stream packet queue
     * @return TSRTPNative instance
     */
    public static synchronized TSRTSPNative getInstance() {
    	if(null == mTsrtpNative) {
    		mTsrtpNative = new TSRTSPNative();
    	}
        return mTsrtpNative;
    }
	    /**
	     * TSRTPNative construction 
	     * @param mRtspUrl 
	     * @param pDatas video stream packet queue
	     */
    private TSRTSPNative() {
//    	this.rtspUrl = mRtspUrl;
//    	mVideoPcmDatas = pVideoDatas;
//    	mAudioPcmDatas = pAudioDatas;
//    	mVideoPcmDatas.clear();
//    	mAudioPcmDatas.clear();
//    	mHandler = handler;
//    	isInitiated = true;
    	executorService = Executors.newSingleThreadExecutor();
    }

	 /**
     * RTP stop video stream packet receiving thread
     */
    public void stop(int channel)
    {
    		Log.e("native", "java call stop");
    		native_deinit(1 << channel);
    }
    
    private void postVideoDataFromNative0(int what, byte[] callbackBuffer,
			long presentationTimeUs) {
    		System.out.println("channel 0 input one video frame!!!! ");
    		if(this.isInitiated1) {
			try {
				if(null != mVideoPcmData1) {
					byte[] data = new byte[what];
					System.arraycopy(callbackBuffer, 0, data, 0, what);
					TSPcmData pcmData = new TSPcmData(data, what, (int)presentationTimeUs);
					mVideoPcmData1.put(pcmData);
					
//					getInstance().putPcmDataToUpload(pcmData, ClientNo.Client_ONE);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
    
    private void postVideoDataFromNative1(int what, byte[] callbackBuffer,
			long presentationTimeUs) {
    		System.out.println("channel 1 input one video frame!!!! ");
    		if(this.isInitiated2) {
			try {
				if(null != mVideoPcmData2) {
					byte[] data = new byte[what];
					System.arraycopy(callbackBuffer, 0, data, 0, what);
					TSPcmData pcmData = new TSPcmData(data, what, (int)presentationTimeUs);
					mVideoPcmData2.put(pcmData);
					
//					getInstance().putPcmDataToUpload(pcmData, ClientNo.Client_TWO);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
    
    private void postVideoDataFromNative2(int what, byte[] callbackBuffer,
			long presentationTimeUs) {
    		System.out.println("channel 2 input one video frame!!!! ");
    		if(this.isInitiated3) {
			try {
				if(null != mVideoPcmData3) {
					byte[] data = new byte[what];
					System.arraycopy(callbackBuffer, 0, data, 0, what);
					TSPcmData pcmData = new TSPcmData(data, what, (int)presentationTimeUs);
					mVideoPcmData3.put(pcmData);
					
//					getInstance().putPcmDataToUpload(pcmData, ClientNo.Client_THREE);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
    private void postAudioDataFromNative0(int what, byte[] callbackBuffer,
        long presentationTimeUs) {
//        System.out.println("channel 0 input one audio frame!!!! ");
    }

    private void postAudioDataFromNative1(int what, byte[] callbackBuffer,
        long presentationTimeUs) {
//        System.out.println("channel 1 input one audio frame!!!! ");
    }

    private void postAudioDataFromNative2(int what, byte[] callbackBuffer,
        long presentationTimeUs) {
//        System.out.println("channel 2 input one audio frame!!!! ");
    }
    
    private void postAudioDataFromNative(int what, byte[] callbackBuffer,
			long presentationTimeUs) {
//		System.out.println("postAudioEventFromNative ");
//		if(null != mAudioPcmData1){
//		byte[] data;
//		if(Constant.isWithSevenByte){
//			data = Arrays.copyOfRange(callbackBuffer,0,callbackBuffer.length); 
//		}else{
//			data = Arrays.copyOfRange(callbackBuffer,7,callbackBuffer.length); 
//		}
//		TSPcmData pcmData = new TSPcmData(data, what, (int)presentationTimeUs);
//		try {
//			mAudioPcmData1.put(pcmData);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		System.out.println("  input 1 frame!!!! ");
//		}
	}
    
    private void exitNotify0() {
        System.out.println("channel 0 exit!!!! ");
    }

    private void exitNotify1() {
        System.out.println("channel 1 exit!!!! ");
    }

    private void exitNotify2() {
        System.out.println("channel 2 exit!!!! ");
    }

	public void start(final ClientNo clientNo) {
		System.out.println("TSRTSPNative start");
		synchronized (executorService) {
			executorService.execute(new Runnable() {
				@Override
				public void run() {
					switch (clientNo) {
					case Client_ONE:
//						native_init("rtmp://192.168.1.138/live/stream", null, rtspUrl1, null, null);
//						native_init("http://push.bcelive.com/live/od5p5ofwzqgxnjrlqn", null, rtspUrl1, null, null);
//						native_init("rtmp://push.bcelive.com/live/od5p5ofwzqgxnjrlqn", null, rtspUrl1, null, null);
//						native_init("rtmp://push.bcelive.com/live/eqo0jv9ohcbu53gcod", null, rtspUrl1, null, null);
//						native_init("rtmp://push.bcelive.com/live/zfmwquw6uq3veisk4u", null, rtspUrl1, null, null);
						native_init(Constant.pushUrl, null, rtspUrl1, null, null);
						break;
					case Client_TWO:
						native_init(null, null, null, rtspUrl2, null);
						break;
					case Client_THREE:
						native_init(null, null, null, null, rtspUrl3);
						break;
					default:
						break;
					}
				}
			});
		}
//		switch (clientNo) {
//		case Client_ONE:
//			native_init("rtmp://192.168.1.10/live/stream", null, rtspUrl1, null, null);
//			break;
//		case Client_TWO:
//			native_init(null, null, null, rtspUrl2, null);
//			break;
//		case Client_THREE:
//			native_init(null, null, null, null, rtspUrl3);
//			break;
//		default:
//			break;
//		}
	}

	public void initClient1(String rtspUrl, SynchronousQueue<TSPcmData> mVideoPcmData, SynchronousQueue<TSPcmData> mAudioPcmData) {
		this.rtspUrl1 = rtspUrl;
		this.mVideoPcmData1 = mVideoPcmData;
		this.mAudioPcmData1 = mAudioPcmData;
		this.isInitiated1 = true;
	}
	
	public void initClient2(String rtspUrl, SynchronousQueue<TSPcmData> mVideoPcmData, SynchronousQueue<TSPcmData> mAudioPcmData) {
		this.rtspUrl2 = rtspUrl;
		this.mVideoPcmData2 = mVideoPcmData;
		this.mAudioPcmData2 = mAudioPcmData;
		this.isInitiated2 = true;
	}
	
	public void initClient3(String rtspUrl, SynchronousQueue<TSPcmData> mVideoPcmData, SynchronousQueue<TSPcmData> mAudioPcmData) {
		this.rtspUrl3 = rtspUrl;
		this.mVideoPcmData3 = mVideoPcmData;
		this.mAudioPcmData3 = mAudioPcmData;
		this.isInitiated3 = true;
	}
	
	public void startUpload(ClientNo clientNo) {
		if(getInstance().isUploading) {
			getInstance().swtchToClientNo = clientNo;
			getInstance().isAboutToSwitchClient = true;
		} else {
			getInstance().curUploadClientNo = clientNo;
			getInstance().isAboutToStartUploading = true;
		}
	}
	
	public void stopUpload() {
		getInstance().isAboutToStartUploading = false;
		getInstance().isUploading = false;
		getInstance().isAboutToSwitchClient = false;
	}
	
	private void putPcmDataToUpload(TSPcmData pcmData, ClientNo clientNo) {
		if(getInstance().isUploading) {
			if(getInstance().isAboutToSwitchClient) {
				if(getInstance().swtchToClientNo.equals(clientNo) && getInstance().isIFrame(pcmData)) {
					getInstance().putFrameDataToQueue(pcmData);
					getInstance().curUploadClientNo = getInstance().swtchToClientNo;
					getInstance().isUploading = true;
					getInstance().isAboutToStartUploading = false;
					getInstance().isAboutToSwitchClient = false;
					getInstance().swtchToClientNo = ClientNo.Client_INVALID;
				} else if(getInstance().curUploadClientNo.equals(clientNo)) {
					getInstance().putFrameDataToQueue(pcmData);
				}
			} else if(getInstance().curUploadClientNo.equals(clientNo)) {
				getInstance().putFrameDataToQueue(pcmData);
			}
		} else if(getInstance().isAboutToStartUploading) {
			if(getInstance().curUploadClientNo.equals(clientNo)) {
				if(getInstance().isIFrame(pcmData)) {
					getInstance().putFrameDataToQueue(pcmData);
					getInstance().isUploading = true;
					getInstance().isAboutToStartUploading = false;
					getInstance().isAboutToSwitchClient = false;
					getInstance().swtchToClientNo = ClientNo.Client_INVALID;
				}
			}
		}
	}
	
	private void putFrameDataToQueue(TSPcmData pcmData) {
		if(getInstance().isSPSAndPPSFrame(pcmData)) {
			TSPcmData[] pcmDatas = getInstance().splitSPSAndPPSFrame(pcmData);
			for(TSPcmData pData : pcmDatas) {
				Constant.queueUpload.offer(pData);
			}
		} else if(getInstance().isIFrame(pcmData)) {
			
		} else {
			byte[] frameData = new byte[pcmData.getSize()];
			System.arraycopy(pcmData.getPcm(), 0, frameData, 0, pcmData.getSize());
			TSPcmData pData = new TSPcmData(frameData, frameData.length, pcmData.getTimeStamp());
			pData.setFrameType(TSPcmData.FRAME_TYPE_P);
			Constant.queueUpload.offer(pData);
		}
		if(null != this.mHandler) {
			this.mHandler.sendEmptyMessage(DeviceDiscoverService.UploadHandlerThread.MSG_DATA_ARRIVED);
		}
	}
	
	private boolean isSPSAndPPSFrame(TSPcmData pcmData) {
		if((pcmData.getPcm()[4] & 0x1f) == TYPE_FRAME_SPS) {
			return true;
		}
		return false;
	}

	private boolean isIFrame(TSPcmData pcmData) {
		if((pcmData.getPcm()[4] & 0x1f) == TYPE_FRAME_I) {
			return true;
		}
		return false;
	}
	
	private TSPcmData[] splitSPSAndPPSFrame(TSPcmData pcmData) {
		int cnt = 0;
		int i = 0;
		int[] pos = new int[3];
		while(cnt < 3 && i < pcmData.getSize()) {
			if(this.checkFrameHead(i, pcmData.getPcm())) {
				pos[cnt++] = i;
			}
			i++;
		}
		TSPcmData[] tsPcmDatas = new TSPcmData[3];
		byte[] spsData = new byte[pos[1] - pos[0]];
		byte[] ppsData = new byte[pos[2] - pos[1]];
		byte[] esiData = new byte[pcmData.getSize() - pos[2]];
		System.arraycopy(pcmData.getPcm(), pos[0], spsData, 0, pos[1] - pos[0]);
		System.arraycopy(pcmData.getPcm(), pos[1], ppsData, 0, pos[2] - pos[1]);
		System.arraycopy(pcmData.getPcm(), pos[2], esiData, 0, pcmData.getSize() - pos[2]);
		int timeStamp = pcmData.getTimeStamp();
		tsPcmDatas[0] = new TSPcmData(spsData, pos[1] - pos[0], timeStamp);
		tsPcmDatas[0].setFrameType(TSPcmData.FRAME_TYPE_SPS);
		tsPcmDatas[1] = new TSPcmData(ppsData, pos[2] - pos[1], timeStamp);
		tsPcmDatas[1].setFrameType(TSPcmData.FRAME_TYPE_PPS);
		tsPcmDatas[2] = new TSPcmData(esiData, pcmData.getSize() - pos[2], timeStamp);
		return tsPcmDatas;
	}
	
	private boolean checkFrameHead(int i, byte[] data) {
		if(data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
			return true;
		}
		return false;
	}

	public void setmHandler(Handler mHandler) {
		this.mHandler = mHandler;
	}
}
